﻿using System.Linq.Dynamic.Core;
using System.Net;
using System.Reflection;
using System.Security.Claims;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Web;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using HttpMethod = Devonline.Core.HttpMethod;
using SameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode;

namespace Devonline.AspNetCore;

public static class AspNetCoreExtensions
{
    #region mvc & api extensions
    /// <summary>
    /// 设置默认的 Json 序列化设置
    /// </summary>
    /// <param name="jsonSerializerOptions"></param>
    /// <param name="httpSetting"></param>
    public static JsonSerializerOptions SetDefaultJsonSerializerOptions(this JsonSerializerOptions jsonSerializerOptions, HttpSetting httpSetting)
    {
        jsonSerializerOptions.AllowTrailingCommas = true;
        jsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
        jsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
        jsonSerializerOptions.IgnoreReadOnlyFields = false;
        jsonSerializerOptions.IgnoreReadOnlyProperties = false;
        jsonSerializerOptions.IncludeFields = false;
        jsonSerializerOptions.MaxDepth = UNIT_FOUR;
        jsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString | JsonNumberHandling.AllowNamedFloatingPointLiterals;
        jsonSerializerOptions.PropertyNameCaseInsensitive = true;
        jsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
        jsonSerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip;
        jsonSerializerOptions.WriteIndented = true;

        //枚举使用字符串表示形式
        jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));

        if (httpSetting.DateTimeToString)
        {
            //自定义日期和时间格式
            jsonSerializerOptions.Converters.Add(new DateTimeConverter(DEFAULT_DATETIME_FORMAT));
            jsonSerializerOptions.Converters.Add(new DateTimeOffsetConverter(DEFAULT_DATETIMEOFFSET_FORMAT));
            jsonSerializerOptions.Converters.Add(new DateOnlyConverter(DEFAULT_DATE_FORMAT));
            jsonSerializerOptions.Converters.Add(new TimeOnlyConverter(DEFAULT_TIME_FORMAT));
        }

        return jsonSerializerOptions;
    }
    /// <summary>
    /// 设置默认的 Json 序列化设置
    /// </summary>
    /// <param name="jsonSerializerSettings"></param>
    /// <param name="httpSetting"></param>
    public static JsonSerializerSettings SetDefaultJsonSerializerSettings(this JsonSerializerSettings jsonSerializerSettings, HttpSetting httpSetting)
    {
        //使用驼峰样式的key
        jsonSerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

        //忽略循环引用
        jsonSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
        jsonSerializerSettings.TypeNameHandling = TypeNameHandling.None;
        jsonSerializerSettings.NullValueHandling = NullValueHandling.Ignore;

        //设置时间格式
        jsonSerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local;
        jsonSerializerSettings.DateParseHandling = DateParseHandling.DateTime;
        jsonSerializerSettings.DateFormatHandling = DateFormatHandling.IsoDateFormat;

        if (httpSetting.DateTimeToString)
        {
            //自定义时间格式
            jsonSerializerSettings.DateFormatString = DEFAULT_DATETIME_FORMAT;
        }

        //枚举类型转字符串输出
        jsonSerializerSettings.Converters.Add(new StringEnumConverter());

        return jsonSerializerSettings;
    }
    /// <summary>
    /// 用于依赖注入的 json 全局设置
    /// 为避免客户端字段名首字母大小写的问题, 将不再使用 CamelCase
    /// </summary>
    /// <param name="builder"></param>
    /// <returns></returns>
    public static IMvcBuilder AddJsonOptions(this IMvcBuilder builder, HttpSetting httpSetting)
    {
        builder.AddJsonOptions(option => option.JsonSerializerOptions.SetDefaultJsonSerializerOptions(httpSetting));
        return builder.AddNewtonsoftJson(option => option.SerializerSettings.SetDefaultJsonSerializerSettings(httpSetting));
    }
    #endregion

    #region Http action results
    /// <summary>
    /// 通用 HTTP 返回值
    /// </summary>
    /// <typeparam name="TValue"></typeparam>
    /// <param name="controller"></param>
    /// <param name="statusCode"></param>
    /// <param name="value"></param>
    /// <param name="message"></param>
    /// <param name="code">返回码</param>
    /// <returns></returns>
    public static IActionResult Result<TValue>(this ControllerBase controller, HttpStatusCode statusCode = HttpStatusCode.OK, TValue? value = default, string? message = default, int? code = null)
    {
        var result = new HttpResult<TValue> { Code = code ?? (int)statusCode, Message = message, Data = value };
        return statusCode switch
        {
            HttpStatusCode.OK => controller.Ok(result),
            HttpStatusCode.BadRequest => controller.BadRequest(result),
            HttpStatusCode.Unauthorized => controller.Unauthorized(result),
            HttpStatusCode.Forbidden => controller.Forbid(),
            HttpStatusCode.NotFound => controller.NotFound(result),
            //HttpStatusCode.MethodNotAllowed => controller.StatusCode(result.Code, result),
            //HttpStatusCode.NotAcceptable => controller.StatusCode(result.Code, result),
            //HttpStatusCode.InternalServerError => controller.StatusCode(result.Code, result),
            _ => controller.StatusCode(result.Code, result),
        };
    }
    /// <summary>
    /// 默认实现的成功但无返回值的请求, 将默认返回 true
    /// </summary>
    /// <param name="controller">当前 controller</param>
    /// <returns></returns>
    public static IActionResult OkResult(this ControllerBase controller) => controller.Ok(new HttpResult<bool> { Code = StatusCodes.Status200OK, Message = nameof(HttpStatusCode.OK), Data = true });
    /// <summary>
    /// 默认实现的返回成功的请求
    /// </summary>
    /// <typeparam name="TValue">返回值类型</typeparam>
    /// <param name="controller">当前 controller</param>
    /// <param name="value">返回值</param>
    /// <returns></returns>
    public static IActionResult OkResult<TValue>(this ControllerBase controller, TValue value) => controller.Ok(new HttpResult<TValue> { Code = StatusCodes.Status200OK, Message = nameof(HttpStatusCode.OK), Data = value });
    /// <summary>
    /// 默认实现的返回部分成功, 可自定义内容的请求
    /// </summary>
    /// <typeparam name="TValue">返回值类型</typeparam>
    /// <param name="controller">当前 controller</param>
    /// <param name="value">返回值</param>
    /// <param name="message">返回消息</param>
    /// <param name="code">返回码</param>
    /// <returns></returns>
    public static IActionResult OkResult<TValue>(this ControllerBase controller, TValue value, string? message = default, int? code = null) => controller.Ok(new HttpResult<TValue> { Code = code ?? StatusCodes.Status200OK, Message = message ?? nameof(HttpStatusCode.OK), Data = value });
    /// <summary>
    /// 默认实现的返回业务不成功的请求
    /// </summary>
    /// <param name="controller"></param>
    /// <param name="message"></param>
    /// <param name="code"></param>
    /// <returns></returns>
    public static IActionResult BadRequestResult(this ControllerBase controller, string message, int? code = null) => controller.BadRequest(new HttpResult<string> { Code = code ?? StatusCodes.Status400BadRequest, Message = message });
    /// <summary>
    /// 默认实现的返回业务不成功的请求
    /// </summary>
    /// <typeparam name="TValue"></typeparam>
    /// <param name="controller"></param>
    /// <param name="value">返回值</param>
    /// <param name="message"></param>
    /// <param name="code"></param>
    /// <returns></returns>
    public static IActionResult BadRequestResult<TValue>(this ControllerBase controller, TValue value, string message, int? code = null) => controller.BadRequest(new HttpResult<TValue> { Code = code ?? StatusCodes.Status400BadRequest, Message = message, Data = value });
    /// <summary>
    /// 按状态码返回
    /// </summary>
    /// <param name="controller"></param>
    /// <param name="message"></param>
    /// <param name="code"></param>
    /// <returns></returns>
    public static IActionResult StatusCodeResult(this ControllerBase controller, string message, int code = StatusCodes.Status500InternalServerError) => controller.StatusCode(code, new HttpResult<string> { Code = code, Message = message ?? nameof(HttpStatusCode.InternalServerError) });
    /// <summary>
    /// 按状态码返回
    /// </summary>
    /// <typeparam name="TValue"></typeparam>
    /// <param name="controller"></param>
    /// <param name="value"></param>
    /// <param name="message"></param>
    /// <param name="code"></param>
    /// <returns></returns>
    public static IActionResult StatusCodeResult<TValue>(this ControllerBase controller, TValue value, string message, int code = StatusCodes.Status500InternalServerError) => controller.StatusCode(code, new HttpResult<TValue> { Code = code, Message = message ?? nameof(HttpStatusCode.InternalServerError), Data = value });
    #endregion

    #region http request extensions
    /// <summary>
    /// 从当前 request 的 Query, Form 和 Cookie 中获取参数的值
    /// TODO 不在使用依次取值 ### 取值顺序 Header -> Query -> Form -> Cookie
    /// </summary>
    /// <typeparam name="T">参数值类型</typeparam>
    /// <param name="request">Http Request</param>
    /// <param name="key">参数的键</param>
    /// <returns></returns>
    public static T? GetFromRequest<T>(this HttpRequest request, string key) where T : IConvertible
    {
        var value = string.Empty;
        if (request.Query.Keys.Contains(key))
        {
            value = request.Query[key];
        }
        else if (request.HasFormContentType && request.Form.Keys.IsNotNullOrEmpty() && request.Form.Keys.Contains(key))
        {
            value = request.Form[key];
        }
        else if (request.Cookies.IsNotNullOrEmpty() && request.Cookies.Keys.Contains(key))
        {
            value = request.Cookies[key];
        }

        if (!string.IsNullOrWhiteSpace(value))
        {
            return value.To<T>();
        }

        return default;
    }
    /// <summary>
    /// 从来自当前 request Header 中获取参数的值
    /// </summary>
    /// <typeparam name="T">参数值类型</typeparam>
    /// <param name="request">Http Request</param>
    /// <param name="key">参数的键</param>
    /// <returns></returns>
    public static T? GetFromRequestHeader<T>(this HttpRequest request, string key) where T : IConvertible
    {
        var value = string.Empty;
        if (request.Headers.IsNotNullOrEmpty() && request.Headers.TryGetValue(key.ToLowerInvariant(), out StringValues headerValue))
        {
            value = headerValue;
        }

        if (!string.IsNullOrWhiteSpace(value))
        {
            return value.To<T>();
        }

        return default;
    }
    /// <summary>
    /// 从来自 gateway 的当前 request Header 中获取参数的值
    /// </summary>
    /// <typeparam name="T">参数值类型</typeparam>
    /// <param name="request">Http Request</param>
    /// <param name="key">参数的键</param>
    /// <returns></returns>
    public static T? GetFromGatewayRequestHeader<T>(this HttpRequest request, string key) where T : IConvertible
    {
        var value = string.Empty;
        if (request.Headers.IsNotNullOrEmpty() && string.IsNullOrWhiteSpace(value) && request.Headers.TryGetValue((HTTP_HEADER_GATEWAY_USER_PREFIX + key).ToLowerInvariant(), out StringValues headerValue))
        {
            value = headerValue;
        }

        if (!string.IsNullOrWhiteSpace(value))
        {
            return value.To<T>();
        }

        return default;
    }
    /// <summary>
    /// 获取查询选项, 将来自 HttpPost 的 OData 查询选项加入到 QueryString 键值对中
    /// </summary>
    /// <param name="request">WebApi 的 HttpRequest 对象</param>
    /// <returns>返回合并后的查询选项</returns>
    public static IEnumerable<KeyValuePair<string, StringValues>> GetRequestOptions(this HttpRequest request)
    {
        var queryString = request.Query.ToList();
        if (request.HasFormContentType && (request.Form.Keys?.Count ?? 0) > 0)
        {
            queryString.AddRange(request.Form);
        }

        if (request.Headers.IsNotNullOrEmpty())
        {
            queryString.AddRange(request.Headers);
        }

        if (request.Cookies.IsNotNullOrEmpty())
        {
            queryString.AddRange(request.Cookies.ToDictionary(x => x.Key, x => new StringValues(x.Value)));
        }

        return queryString;
    }
    /// <summary>
    /// 从当前 request 中获取参数的值, 取值顺序: 当前 request 的 Query, Form 和 Cookie, 当前 request 的 header, 来自 gateway 的 request header
    /// 其中一旦 Header 中有值, 会无视/覆盖其他集合中已经读取的值
    /// </summary>
    /// <typeparam name="T">参数值类型</typeparam>
    /// <param name="request">Http Request</param>
    /// <param name="key">参数的键</param>
    /// <returns></returns>
    public static T? GetRequestOption<T>(this HttpRequest request, string key) where T : IConvertible => request.GetFromRequest<T>(key)
        ?? request.GetFromRequestHeader<T>(key)
        ?? request.GetFromGatewayRequestHeader<T>(key);
    /// <summary>
    /// 以最高优先级设置 Request 中的查询选项
    /// </summary>
    /// <param name="request"></param>
    /// <param name="key"></param>
    /// <param name="value"></param>
    public static void SetRequestHeader(this HttpRequest request, string key, string value) => request.Headers[key] = value;
    /// <summary>
    /// 限制请求的分页中每页查询结果返回的行数
    /// </summary>
    /// <param name="request"></param>
    /// <param name="pageSize"></param>
    public static void LimitRequestPageSize(this HttpRequest request, int pageSize = DEFAULT_PAGE_SIZE)
    {
        var value = pageSize.ToString();
        request.SetRequestHeader(QUERY_OPTION_TOP, value);
        request.SetRequestHeader(nameof(PagedRequest.PageSize).ToCamelCase(), value);
    }
    /// <summary>
    /// 获取请求的 origin 字符串
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    public static string GetRequestHost(this HttpRequest request) => new(new StringBuilder()
         .Append(request.Scheme)
         .Append(DEFAULT_PROTOCOL_SPLITER)
         .Append(request.Host)
         .ToString());
    /// <summary>
    /// 获取请求的 path 字符串, 不带请求参数的部分
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    public static string GetRequestPath(this HttpRequest request)
    {
        var builder = new StringBuilder()
             .Append(request.Scheme)
             .Append(DEFAULT_PROTOCOL_SPLITER)
             .Append(request.Host)
             .Append(request.PathBase);
        if (request.Path.HasValue && request.Path != CHAR_SLASH.ToString())
        {
            builder.Append(request.Path);
        }

        return new(builder.ToString());
    }
    /// <summary>
    /// 获取请求的完整 uri 字符串
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    public static string GetRequestUri(this HttpRequest request)
    {
        var builder = new StringBuilder()
             .Append(request.Scheme)
             .Append(DEFAULT_PROTOCOL_SPLITER)
             .Append(request.Host)
             .Append(request.PathBase);
        if (request.Path.HasValue && request.Path != CHAR_SLASH.ToString())
        {
            builder.Append(request.Path).Append(request.QueryString);
        }

        return new(builder.ToString());
    }
    /// <summary>
    /// 获取请求的 IP 地址
    /// </summary>
    /// <param name="request"></param
    /// <param name="header"></param>
    /// <returns></returns>
    public static string GetRequestIpAddress(this HttpRequest request, string? header = default) => request.Headers[header ?? HTTP_HEADER_FORWARDED_FOR].FirstOrDefault()
        ?? request.Headers[HTTP_HEADER_REAL_IP].FirstOrDefault()
        ?? request.HttpContext.Connection.RemoteIpAddress?.ToString()
        ?? string.Empty;
    /// <summary>
    /// 获取请求的 IPv4 地址
    /// </summary>
    /// <param name="request"></param
    /// <param name="header"></param>
    /// <returns></returns>
    public static string GetRequestIpv4Address(this HttpRequest request, string? header = default) => request.Headers[header ?? HTTP_HEADER_FORWARDED_FOR].FirstOrDefault()
        ?? request.Headers[HTTP_HEADER_REAL_IP].FirstOrDefault()
        ?? request.HttpContext.Connection.RemoteIpAddress?.MapToIPv4().ToString()
        ?? string.Empty;

    /// <summary>
    /// 从 http response 接口返回值中获取 Header 中的 Cookie 部分
    /// </summary>
    /// <param name="response"></param>
    /// <returns></returns>
    public static ICollection<SetCookieHeaderValue> GetSetCookies(this HttpResponseMessage response)
    {
        var setCookies = new List<SetCookieHeaderValue>();
        if (response.Headers.TryGetValues("Set-Cookie", out var values))
        {
            foreach (var cookie in values)
            {
                if (SetCookieHeaderValue.TryParse(cookie, out SetCookieHeaderValue? setCookie) && setCookie is not null)
                {
                    setCookies.Add(setCookie);
                }
            }
        }

        return setCookies;
    }
    /// <summary>
    /// 将 SetCookie 转换为 Cookie
    /// </summary>
    /// <param name="setCookie"></param>
    /// <returns></returns>
    public static CookieOptions GetCookieOptions(this SetCookieHeaderValue setCookie) => new()
    {
        Domain = setCookie.Domain.ToString(),
        Path = setCookie.Path.ToString(),
        Expires = setCookie.Expires,
        HttpOnly = setCookie.HttpOnly,
        MaxAge = setCookie.MaxAge,
        Secure = setCookie.Secure,
        SameSite = (SameSiteMode)setCookie.SameSite
    };

    /// <summary>
    /// 获取请求中的 columns 字段列表并转换为字符串数组
    /// </summary>
    /// <typeparam name="T">字段存在的校验类型</typeparam>
    /// <param name="request">Http 请求</param
    /// <param name="useCamelCase">是否使用驼峰法</param>
    /// <returns></returns>
    public static IEnumerable<string>? GetRequestColumns<T>(this HttpRequest request, bool useCamelCase = true)
    {
        string? columns = request.GetRequestOption<string>(nameof(columns));
        if (columns is not null)
        {
            var propertyInfos = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            var columnNames = columns.Split(CHAR_COMMA, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
            if (columnNames is not null && columnNames.Length > 0)
            {
                if (useCamelCase)
                {
                    columnNames = columnNames.Select(x => x.FirstCharToUpper()).ToArray();
                }

                var notExists = columnNames.Where(x => !propertyInfos.Any(a => a.PropertyType.GetCoreType().IsSampleType() && a.Name == x));
                if (notExists.Any())
                {
                    throw new HttpRequestException($"请求参数 columns 中, 列 {notExists.ToString<string>()} 不存在!");
                }

                return columnNames;
            }
        }

        return null;
    }
    /// <summary>
    /// 通过 request query 获取 QueryOptions 的实例
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="request"></param>
    /// <returns></returns>
    public static QueryOptions<T> GetQueryOptions<T>(this HttpRequest request) => new(new QueryOptionRawValue(request.Query));
    /// <summary>
    /// http request 基础分页请求参数
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    public static PagedRequest GetPagedRequest(this HttpRequest request)
    {
        var pagedRequest = new PagedRequest
        {
            Total = request.GetRequestOption<bool>(nameof(PagedRequest.Total).ToCamelCase()),
            PageIndex = request.GetRequestOption<int>(nameof(PagedRequest.PageIndex).ToCamelCase()),
            PageSize = request.GetRequestOption<int>(nameof(PagedRequest.PageSize).ToCamelCase()),
            Orderby = request.GetRequestOption<string>(nameof(PagedRequest.Orderby).ToCamelCase())
        };

        if (pagedRequest.PageIndex <= UNIT_ONE)
        {
            pagedRequest.PageIndex = request.GetRequestOption<int>(nameof(PagedRequest.PageIndex).ToLowerInvariant());
            if (pagedRequest.PageIndex <= UNIT_ONE)
            {
                //增加 ant design 分页的 current 支持
                pagedRequest.PageIndex = request.GetRequestOption<int>(nameof(AntDesign.AntDesignData.Current).ToLowerInvariant());
                if (pagedRequest.PageIndex <= UNIT_ONE)
                {
                    pagedRequest.PageIndex = DEFAULT_PAGE_INDEX;
                }
            }
        }

        if (pagedRequest.PageSize <= UNIT_ONE)
        {
            pagedRequest.PageSize = request.GetRequestOption<int>(nameof(PagedRequest.PageSize).ToLowerInvariant());
            if (pagedRequest.PageSize <= UNIT_ONE)
            {
                pagedRequest.PageSize = DEFAULT_PAGE_SIZE;
            }
        }

        return pagedRequest;
    }
    /// <summary>
    /// 根据上下文和请求中的分页条件获取分页的查询结果
    /// 现在支持在 url 中使用 odata 表达式, 类 odata 的兼容表达式(待加强) 和非 odata 表达式(原始的键值对形式)
    /// odata 和类 odata 表达式遵循 odata v4 表达式规范
    /// 非 odata 的表达式查询条件部分使用如下规则:
    /// 1. 使用 filter=true/false 作为启动开关, 可由客户端, 服务器设置, 全局默认开关在 HttpSetting 中由: AutoFilter 配置, 默认为: false, 不启用
    /// 2. 在 filter=true 的情况下, 自动过滤的字段来自 Request.QueryString, 字段名必须存在于当前操作的数据类型泛型成员中
    /// 3. 字段和值的操作关系, 由参数 filterOperators 定义, 默认的操作类型由 客户端, 服务器设定, 全局默认设置在 HttpSetting 中由: DefaultOperatorType 配置, 默认为: OperatorType.Contains, 包含关系, 即: 模糊匹配
    /// 4. 继承自 IRange 接口的实现类型和其他特殊类型的约定操作关系请参见: ToFilterOption 方法定义, 其余类型使用 eq, 相等关系
    /// </summary>
    /// <typeparam name="T">操作的数据类型</typeparam>
    /// <param name="request">Http 请求</param>
    /// <param name="queryable">针对数据的查询表达式</param>
    /// <param name="filterOperators">特殊情况下, 使用全局自动条件过滤的时候, 描述字段和值的逻辑关系</param>
    /// <returns></returns>
    public static async Task<PagedResult<T>> GetPagedResultAsync<T>(this HttpRequest request, IQueryable<T> queryable, Dictionary<string, OperatorType>? filterOperators = null) where T : class, new()
    {
        //如果 count 查询表达式不传值, 则不分页
        var pagedRequest = request.GetPagedRequest();
        var queryOptions = request.GetQueryOptions<T>();
        if (queryOptions.Count.HasValue && queryOptions.Count.Value)
        {
            //odata 表达式的支持形式
            pagedRequest = queryOptions;
            pagedRequest.Orderby = queryOptions.Orderby?.ToString();
        }
        else
        {
            //非 odata 表达式的支持形式
            PagedOptions pagedOptions = pagedRequest;
            queryOptions.Count = pagedOptions.Count;
            queryOptions.Skip = pagedOptions.Skip;
            queryOptions.Top = pagedOptions.Top;
            if (!string.IsNullOrWhiteSpace(pagedRequest.Orderby))
            {
                queryOptions.Orderby = new OrderbyOption<T>(pagedRequest.Orderby);
            }

            var queryString = request.QueryString.ToString();
            if (queryString.Contains(CHAR_QUESTION))
            {
                queryString = queryString[UNIT_ONE..];
            }

            if (!string.IsNullOrWhiteSpace(queryString))
            {
                var filter = request.GetRequestOption<string>(QUERY_REQUEST_FILTER);
                var keyValuePairs = queryString.ToKeyValuePairs(DEFAULT_URL_OUTER_SPLITER, DEFAULT_URL_INNER_SPLITER);
                if (!string.IsNullOrWhiteSpace(filter) && keyValuePairs.Count > 0)
                {
                    filter = filter.ToLowerInvariant();
                    if (filter != DEFAULT_VALUE_FALSE)
                    {
                        var filters = filter.Split(CHAR_COMMA, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
                        if (filter != DEFAULT_VALUE_TRUE)
                        {
                            keyValuePairs = keyValuePairs.Where(x => filters.Contains(x.Key.ToLowerInvariant())).ToArray();
                        }

                        if (keyValuePairs.Count > 0)
                        {
                            var operatorType = request.GetRequestOption<OperatorType>(nameof(HttpSetting.DefaultOperatorType).ToCamelCase());
                            queryOptions.Filter = keyValuePairs.ToFilterOption<T>(operatorType, filterOperators);
                        }
                    }
                }
            }
        }

        if (queryOptions.Filter is not null)
        {
            if (queryOptions.Filter is NestedFilterOption<T> filter && filter.Filters is not null && filter.Filters.Any())
            {
                //目前仅支持一级嵌套, 且顶级条件仅支持 and
                //var expression = string.Join(GetFilterLogic(filter.Logic ?? LogicType.And), filter.Filters.Select(x => GetFilterExpression(x)));
                queryable = queryable.Where(string.Join(filter.GetFilterLogic(), filter.Filters.Select(x => x.GetFilterExpression())));
            }
            else if ((!string.IsNullOrWhiteSpace(queryOptions.Filter.Field)) && (!string.IsNullOrWhiteSpace(queryOptions.Filter.Value)))
            {
                queryable = queryable.Where(queryOptions.Filter.GetFilterExpression(), queryOptions.Filter.GetFilterValue());
            }
        }

        if (queryOptions.Expand is not null)
        {
            var columns = queryOptions.Expand.ToString().Split(CHAR_COMMA, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
            if (columns.Length > 0)
            {
                foreach (var column in columns)
                {
                    queryable = queryable.Include(column);
                }
            }
        }

        if (queryOptions.Orderby is not null)
        {
            queryable = queryable.OrderBy(queryOptions.Orderby.ToString());
        }

        //不分页时, 页大小将返回 0
        var result = new PagedResult<T> { PageIndex = pagedRequest.PageIndex, PageSize = queryOptions.Count.HasValue ? pagedRequest.PageSize : UNIT_ZERO };
        var canAsync = true;

        try
        {
            result.Total = await queryable.CountAsync();
        }
        catch (Exception)
        {
            canAsync = false;
            result.Total = queryable.Count();
        }

        if (queryOptions.Count.HasValue && queryOptions.Count.Value)
        {
            if (queryOptions.Skip.HasValue && queryOptions.Skip.Value > 0)
            {
                queryable = queryable.Skip(queryOptions.Skip.Value);
            }

            if (queryOptions.Top.HasValue && queryOptions.Top.Value > 0)
            {
                queryable = queryable.Take(queryOptions.Top.Value);
            }
        }

        result.Data = canAsync ? await queryable.ToListAsync() : queryable.ToList();
        return result;
    }
    #endregion

    #region http context extensions
    /// <summary>
    /// 从 claims 中获取第一个类型为 type 的值
    /// </summary>
    /// <param name="claims"></param>
    /// <param name="type"></param>
    /// <param name="provider"></param>
    /// <returns></returns>
    public static string? GetClaimValue(this IEnumerable<Claim> claims, string type, string? provider = default)
    {
        string? value = null;
        if (!string.IsNullOrWhiteSpace(provider))
        {
            value = claims.FirstOrDefault(x => x.Type.ToLowerInvariant().EndsWith((provider + CHAR_COLON + type).ToLowerInvariant()))?.Value;
        }

        value ??= claims.FirstOrDefault(x => x.Type.ToLowerInvariant() == type.ToLowerInvariant())?.Value;
        return value;
    }
    /// <summary>
    /// 获取上下文对象中指定 Claim type 的值
    /// </summary>
    /// <typeparam name="T">声明值的类型</typeparam>
    /// <param name="httpContext">Http 上下文</param>
    /// <param name="type">声明类型</param>
    /// <returns></returns>
    public static T? GetClaimValue<T>(this HttpContext httpContext, string type) where T : IConvertible
    {
        var value = httpContext.User.Claims.FirstOrDefault(x => x.Type.ToLowerInvariant() == type.ToLowerInvariant())?.Value;
        return !string.IsNullOrWhiteSpace(value) ? value.To<T>() : default;
    }
    /// <summary>
    /// 获取上下文对象中参数/变量的值, 先取 http context 的 Claims, 后取 request
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="httpContext"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static T? GetFromHttpContext<T>(this HttpContext httpContext, string key) where T : IConvertible => httpContext.GetClaimValue<T>(key) ?? httpContext.Request.GetRequestOption<T>(key);
    /// <summary>
    /// 从 httpContext 获取用户标识, 用户标识取值顺序为: 在 User.Claims 中的 type 为 sub; 在 request 中为 userId
    /// User.Claims 中的 type 为 userId 的值 -> User.Claims 中的 type 为 id 的值 -> User.Claims 中的 type 为 sub 的值
    /// 不在主动获取 request 中 userId 的参数值
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public static T? GetUserId<T>(this HttpContext httpContext) where T : IConvertible => httpContext.GetFromHttpContext<T>(CLAIM_TYPE_USER_ID)
        ?? httpContext.GetClaimValue<T>(CLAIM_TYPE_JWT_SUBJECT)
        ?? httpContext.GetClaimValue<T>(CLAIM_TYPE_ID)
        ?? httpContext.GetClaimValue<T>(ClaimTypes.PrimarySid)
        ?? httpContext.GetClaimValue<T>(ClaimTypes.Sid);
    /// <summary>
    /// 从 httpContext 获取用户标识, 用户标识取值顺序为: 在 User.Claims 中的 type 为 sub; 在 request 中为 userId
    /// User.Claims 中的 type 为 userId 的值 -> User.Claims 中的 type 为 id 的值 -> User.Claims 中的 type 为 sub 的值
    /// 不在主动获取 request 中 userId 的参数值
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public static T? GetGroupId<T>(this HttpContext httpContext) where T : IConvertible => httpContext.GetFromHttpContext<T>(CLAIM_TYPE_GROUP_ID)
        ?? httpContext.GetClaimValue<T>(ClaimTypes.PrimaryGroupSid)
        ?? httpContext.GetClaimValue<T>(ClaimTypes.GroupSid);
    /// <summary>
    /// 从 httpContext 获取用户标识, 用户标识取值顺序为: 在 User.Claims 中的 type 为 sub; 在 request 中为 userId
    /// User.Claims 中的 type 为 userId 的值 -> User.Claims 中的 type 为 id 的值 -> User.Claims 中的 type 为 sub 的值
    /// 不在主动获取 request 中 userId 的参数值
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public static string? GetUserId(this HttpContext httpContext) => httpContext.GetUserId<string>();
    /// <summary>
    /// 从 httpContext 获取用户标识, 用户标识取值顺序为: 在 User.Claims 中的 type 为 sub; 在 request 中为 userId
    /// User.Claims 中的 type 为 userId 的值 -> User.Claims 中的 type 为 id 的值 -> User.Claims 中的 type 为 sub 的值
    /// 不在主动获取 request 中 userId 的参数值
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public static string? GetGroupId(this HttpContext httpContext) => httpContext.GetGroupId<string>();
    /// <summary>
    /// 从 httpContext 获取用户名, 用户名取值顺序为:
    /// User.Claims 中的 type 为 userName 的值 -> User.Identity.Name -> User.Claims 中的 type 为 name, Name 的值, 无值则取 anonymous
    /// 不在主动获取 request 中 userName 的参数值
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public static string GetUserName(this HttpContext httpContext) => httpContext.GetFromHttpContext<string>(CLAIM_TYPE_USER_NAME)
        ?? httpContext.User?.Identity?.Name
        ?? httpContext.GetClaimValue<string>(CLAIM_TYPE_NAME)
        ?? httpContext.GetClaimValue<string>(ClaimTypes.Name)
        ?? USER_ANONYMOUS;
    /// <summary>
    /// 获取用户标识的关键字段值, 这个值, 在认证过程中存在于 claim 中
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public static string? GetUserIdentifier(this HttpContext httpContext) => httpContext.GetFromHttpContext<string>(CLAIM_TYPE_USER_IDENTIFIER);
    /// <summary>
    /// 获取用户 NameIdentifier 标志名称, NameIdentifier, 为用户真正的姓名
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public static string? GetUserNameIdentifier(this HttpContext httpContext) => httpContext.GetFromHttpContext<string>(CLAIM_TYPE_USER_NAME_IDENTIFIER) ?? httpContext.GetClaimValue<string>(ClaimTypes.NameIdentifier);

    /// <summary>
    /// 向 HttpContext 添加 Claim, 如添加的 Claim 已经存在, 则会删除原有的
    /// </summary>
    /// <param name="httpContext">Http 上下文</param>
    /// <param name="claims">待添加的 Claim 集合</param>
    public static void AddClaims(this HttpContext httpContext, params Claim[] claims)
    {
        if (claims.Length == 0)
        {
            return;
        }

        ClaimsIdentity? claimsIdentity = null;
        var types = claims.Select(x => x.Type);
        foreach (var identity in httpContext.User.Identities)
        {
            var claimExists = identity.FindAll(x => types.Contains(x.Type)).ToList();
            if (claimExists.Any())
            {
                claimsIdentity = identity;
                for (int index = 0; index < claimExists.Count; index++)
                {
                    identity.TryRemoveClaim(claimExists[index]);
                }
            }
        }

        if (claimsIdentity is null)
        {
            claimsIdentity = httpContext.User.Identities.FirstOrDefault();
        }

        if (claimsIdentity is not null)
        {
            claimsIdentity.AddClaims(claims);
        }
    }

    /// <summary>
    /// 获取请求的 IP 地址
    /// </summary>
    /// <param name="httpContext"></param
    /// <param name="header"></param>
    /// <returns></returns>
    public static string GetRequestIpAddress(this HttpContext httpContext, string? header = default) => httpContext.Request.Headers[header ?? HTTP_HEADER_FORWARDED_FOR].FirstOrDefault()
        ?? httpContext.Request.Headers[HTTP_HEADER_REAL_IP].FirstOrDefault()
        ?? httpContext.Connection.RemoteIpAddress?.ToString()
        ?? string.Empty;
    /// <summary>
    /// 获取请求的 IPv4 地址
    /// </summary>
    /// <param name="httpContext"></param
    /// <param name="header"></param>
    /// <returns></returns>
    public static string GetRequestIpv4Address(this HttpContext httpContext, string? header = default) => httpContext.Request.Headers[header ?? HTTP_HEADER_FORWARDED_FOR].FirstOrDefault()
        ?? httpContext.Request.Headers[HTTP_HEADER_REAL_IP].FirstOrDefault()
        ?? httpContext.Connection.RemoteIpAddress?.MapToIPv4().ToString()
        ?? string.Empty;
    #endregion

    #region 其他扩展
    /// <summary>
    /// 根据 HttpMethodAttribute 获取 HttpMethod 枚举值
    /// </summary>
    /// <param name="httpMethod">HttpMethodAttribute</param>
    /// <returns></returns>
    public static HttpMethod GetHttpMethod(this HttpMethodAttribute httpMethod) => httpMethod switch
    {
        HttpGetAttribute => HttpMethod.Get,
        HttpPostAttribute => HttpMethod.Post,
        HttpPutAttribute => HttpMethod.Put,
        HttpDeleteAttribute => HttpMethod.Delete,
        HttpHeadAttribute => HttpMethod.Head,
        HttpOptionsAttribute => HttpMethod.Options,
        HttpPatchAttribute => HttpMethod.Patch,
        _ => HttpMethod.Trace
    };
    /// <summary>
    /// 根据 HttpMethodAttribute 集合获取 HttpMethod 枚举值
    /// </summary>
    /// <param name="httpMethods">设置在 Action 上的 HttpMethodAttribute 集合</param>
    /// <returns></returns>
    public static HttpMethod? GetHttpMethod(this IEnumerable<HttpMethodAttribute> httpMethods)
    {
        if (httpMethods.Any())
        {
            HttpMethod? httpMethod = null;
            foreach (var attribute in httpMethods)
            {
                if (httpMethod is null)
                {
                    httpMethod = attribute.GetHttpMethod();
                }
                else
                {
                    httpMethod |= attribute.GetHttpMethod();
                }
            }

            return httpMethod;
        }

        return null;
    }

    /// <summary>
    /// 将对象和其值转换为 FilterOption 表达式
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="keyValuePairs"></param>
    /// <param name="operatorType">默认情况下, 使用的过滤条件字段和值的逻辑关系</param>
    /// <param name="filterOperators">特殊情况下, 使用全局自动条件过滤的时候, 描述字段和值的逻辑关系</param>
    /// <returns></returns>
    public static FilterOption<T>? ToFilterOption<T>(this ICollection<KeyValuePair<string, string>> keyValuePairs, OperatorType operatorType = OperatorType.Contains, Dictionary<string, OperatorType>? filterOperators = null)
    {
        var type = typeof(T);
        var propertyInfos = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => keyValuePairs.Select(a => a.Key).Contains(x.Name.ToCamelCase()));
        if (propertyInfos.Any())
        {
            var filters = new List<string>();
            foreach (var propertyInfo in propertyInfos)
            {
                var value = keyValuePairs.FirstOrDefault(x => x.Key == propertyInfo.Name.ToCamelCase()).Value;
                if (!string.IsNullOrWhiteSpace(value))
                {
                    value = HttpUtility.UrlDecode(value);
                    var isRangeType = type.IsFromType<IDateTimeRange>() && propertyInfo.PropertyType == typeof(DateTime?)
                        || type.IsFromType<IDateRange>() && propertyInfo.PropertyType == typeof(DateOnly?)
                        || type.IsFromType<ITimeRange>() && propertyInfo.PropertyType == typeof(TimeOnly?)
                        || type.IsFromType<IIntegerRange>() && propertyInfo.PropertyType == typeof(int?)
                        || type.IsFromType<IDecimalRange>() && propertyInfo.PropertyType == typeof(decimal?);

                    if (isRangeType && propertyInfo.Name == nameof(IDateTimeRange.Start))
                    {
                        filters.Add($"{propertyInfo.Name} {OperatorType.GreaterThanAndEqual.GetJsonPropertyName()} {value}");
                    }
                    else if (isRangeType && propertyInfo.Name == nameof(IDateTimeRange.End))
                    {
                        filters.Add($"{propertyInfo.Name} {OperatorType.LessThan.GetJsonPropertyName()} {value}");
                    }
                    else if (propertyInfo.PropertyType == typeof(string))
                    {
                        var fieldOperatorType = operatorType;
                        if (filterOperators is not null && filterOperators.ContainsKey(propertyInfo.Name))
                        {
                            fieldOperatorType = filterOperators[propertyInfo.Name];
                        }

                        filters.Add(propertyInfo.Name + CHAR_SPACE + fieldOperatorType.GetJsonPropertyName() + CHAR_SPACE + CHAR_QUOTE + value + CHAR_QUOTE);
                    }
                    else if (propertyInfo.PropertyType.IsEnum)
                    {
                        filters.Add(propertyInfo.Name + CHAR_SPACE + OperatorType.Equal.GetJsonPropertyName() + CHAR_SPACE + CHAR_QUOTE + value + CHAR_QUOTE);
                    }
                    else
                    {
                        filters.Add(propertyInfo.Name + CHAR_SPACE + OperatorType.Equal.GetJsonPropertyName() + CHAR_SPACE + value);
                    }
                }
            }

            if (filters.Count > 0)
            {
                if (filters.Count == UNIT_ONE)
                {
                    return new FilterOption<T>(filters.First());
                }
                else
                {
                    return new NestedFilterOption<T>(filters.ToString(CHAR_SPACE + LogicType.And.GetJsonPropertyName() + CHAR_SPACE));
                }
            }
        }

        return null;
    }
    /// <summary>
    /// 枚举按顺序生成基础数据
    /// </summary>
    /// <typeparam name="TEnum"></typeparam>
    /// <param name="index">当前枚举的分配编号</param>
    /// <param name="toLower">key 的值是否使用整体小写</param>
    /// <returns></returns>
    public static List<Parameter> GetParametersFromEnum<TEnum>(this int index, bool toLower = false) where TEnum : struct, Enum
    {
        var type = typeof(TEnum);
        var parameter = new Parameter
        {
            Id = index.ToString(),
            Index = index,
            ParentId = "0",
            Key = type.Name.ToCamelCase(),
            Value = type.Name,
            Text = type.GetDisplayName()
        };

        index = 0;
        parameter.Create();
        var list = new List<Parameter> { parameter };
        var values = Enum.GetValues<TEnum>();
        foreach (var @enum in values)
        {
            var enumValue = Convert.ToInt32(@enum);
            var id = parameter.Index * UNIT_THOUSAND + enumValue;
            if (enumValue == index++)
            {
                id++;
            }

            var value = @enum.ToString();
            var child = new Parameter
            {
                Id = id.ToString(),
                Index = id,
                ParentId = parameter.Id,
                Key = toLower ? value.ToLowerInvariant() : value.ToCamelCase(),
                Value = value,
                Text = @enum.GetDisplayName()!
            };

            child.Create();
            list.Add(child);
        }

        return list;
    }
    /// <summary>
    /// bool 型枚举按顺序生成基础数据
    /// </summary>
    /// <typeparam name="TEnum"></typeparam>
    /// <param name="index">当前枚举的分配编号</param>
    /// <param name="toLower">key 的值是否使用整体小写</param>
    /// <returns></returns>
    public static List<Parameter> GetBoolParametersFromEnum<TEnum>(this int index, bool toLower = false) where TEnum : struct, Enum
    {
        var type = typeof(TEnum);
        var parameter = new Parameter
        {
            Id = index.ToString(),
            Index = index,
            ParentId = "0",
            Key = type.Name.ToCamelCase(),
            Value = type.Name,
            Text = type.GetDisplayName()
        };

        index = 0;
        parameter.Create();
        var list = new List<Parameter> { parameter };
        var values = Enum.GetValues<TEnum>();
        if (values.Length != 2 && values.Length != 3)
        {
            throw new Exception("作为 bool 值的枚举项只能有两个或三个!");
        }

        foreach (var @enum in values)
        {
            var enumValue = Convert.ToInt32(@enum);
            var value = @enum.ToString();
            var id = parameter.Index * UNIT_THOUSAND + enumValue;
            if (enumValue == index++)
            {
                id++;
            }

            var child = new Parameter
            {
                Id = id.ToString(),
                Index = id,
                ParentId = parameter.Id,
                Key = toLower ? value.ToLowerInvariant() : value.ToCamelCase(),
                Text = @enum.GetDisplayName()!
            };

            if (enumValue == 0)
            {
                child.Value = false.ToString().ToLower();
            }
            else if (enumValue == 1)
            {
                child.Value = true.ToString().ToLower();
            }
            else
            {
                child.Value = "null";
            }

            child.Create();
            list.Add(child);
        }

        return list;
    }

    /// <summary>
    /// 根据总数目, 页大小, 获取总页数
    /// </summary>
    /// <param name="total">总数目</param>
    /// <param name="size">页大小</param>
    /// <returns></returns>
    public static long GetPageCount(this long total, int size) => ((total - 1) / size) + 1;
    /// <summary>
    /// 获取当前进度的百分比
    /// </summary>
    /// <param name="total">总数目</param>
    /// <param name="index">当前数目</param>
    /// <returns></returns>
    public static double GetPercent(this long total, int index) => Math.Round(index * 100 / (float)total);

    /// <summary>
    /// 从集合中查找编号对应的顶级父级对象
    /// </summary>
    /// <typeparam name="TEntitySet">数据对象模型的类型</typeparam>
    /// <typeparam name="TKey">主键类型</typeparam>
    /// <param name="entitySets">当前数据对象集合</param>
    /// <param name="id">当前主键编号</param>
    /// <returns></returns>
    public static TEntitySet? GetTopParent<TEntitySet, TKey>(this IEnumerable<TEntitySet> entitySets, TKey id) where TEntitySet : class, IParent<TKey>, IEntitySet<TKey> where TKey : IConvertible
    {
        var current = entitySets.FirstOrDefault(x => x.Id.Equals(id));
        if (current != null)
        {
            if (current.ParentId is null)
            {
                return current;
            }

            return entitySets.GetTopParent<TEntitySet, TKey>(current.ParentId);
        }

        var parent = entitySets.FirstOrDefault(x => x.ParentId != null && x.ParentId.Equals(id));
        if (parent is null)
        {
            return null;
        }

        if (parent.ParentId is null)
        {
            return parent;
        }

        return entitySets.GetTopParent<TEntitySet, TKey>(parent.ParentId);
    }
    /// <summary>
    /// 从集合中查找编号对应的顶级父级对象
    /// </summary>
    /// <typeparam name="TEntitySet">数据对象模型的类型</typeparam>
    /// <param name="entitySets">当前数据对象集合</param>
    /// <param name="id">当前主键编号</param>
    /// <returns></returns>
    public static TEntitySet? GetTopParent<TEntitySet>(this IEnumerable<TEntitySet> entitySets, string id) where TEntitySet : class, IParent, IEntitySet => entitySets.GetTopParent<TEntitySet, string>(id);
    #endregion
}